package com.surpassun.emall.repository;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Restrictions;
import org.hibernate.internal.SessionImpl;
import org.springframework.stereotype.Repository;

import com.surpassun.emall.util.QueryUtil;
import com.surpassun.emall.util.Validator;

/**
 *
 * @author Denis
 */
@Repository
public class AbstractRepository<T> {
	
	@PersistenceContext(unitName = "emall-pu")
    private EntityManager em;
	
    private Class<T> entityClass;
    
    public AbstractRepository() {
    }

    public AbstractRepository(Class<T> entityClass) {
        this.entityClass = entityClass;
    }

    public void add(T entity) {
    	getEntityManager().persist(entity);
    }

    public void update(T entity) {
    	getEntityManager().merge(entity);
    }

    public void delete(T entity) {
    	getEntityManager().remove(getEntityManager().merge(entity));
    }

    public T get(long id) {
        return getEntityManager().find(entityClass, id);
    }
    
    @SuppressWarnings({ "rawtypes", "unchecked" })
	public List<T> getAll() {
        CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
        cq.select(cq.from(entityClass));
        return getEntityManager().createQuery(cq).getResultList();
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    public List<T> getRange(int start, int end) {
        CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
        cq.select(cq.from(entityClass));
        Query q = getEntityManager().createQuery(cq);
        q.setMaxResults(end - start);
        q.setFirstResult(start);
        return q.getResultList();
    }
    
    public T getSingleByField(String fieldName, Object fieldValue, String restrictionType) {
    	List<T> results = getByFields(new String[]{fieldName}, new Object[]{fieldValue}, new String[]{restrictionType});
    	if (Validator.isNotEmpty(results)) {
    		return results.get(0);
    	} else {
    		return null;
    	}
    }
    
	public List<T> getByField(String fieldName, Object fieldValue, String restrictionType) {
    	return getByFields(new String[]{fieldName}, new Object[]{fieldValue}, new String[]{restrictionType});
    }
    
    @SuppressWarnings("unchecked")
	public List<T> getByFields(String[] fieldNames, Object[] fieldValues, String[] restrictionTypes) {
    	if (Validator.isNotEmpty(fieldNames) && Validator.isNotEmpty(fieldValues) && Validator.isNotEmpty(restrictionTypes)
    			&& fieldNames.length == fieldValues.length && fieldNames.length == restrictionTypes.length) {
    		Session session = null;
    		try {
    			session = getHibernateSession();
    			if (Validator.isNotNull(session)) {
    				Criteria criteria = session.createCriteria(this.entityClass);
    				Criterion[] criterion = new Criterion[fieldNames.length];
    				for (int i = 0; i < fieldNames.length; i++) {
    					String fieldName = fieldNames[i];
    					Object fieldValue = fieldValues[i];
    					String restrictionType = restrictionTypes[i];
    					
    					if (Validator.isNotNull(restrictionType)) {
    						if (restrictionType.equals(QueryUtil.EQUAL)) {
    							criterion[i] = Restrictions.eq(fieldName, fieldValue);
    						} else if (restrictionType.equals(QueryUtil.LIKE)) {
    							criterion[i] = Restrictions.like(fieldName, fieldValue);
    						} else if (restrictionType.equals(QueryUtil.GREATER_THAN)) {
    							criterion[i] = Restrictions.gt(fieldName, fieldValue);
    						} else if (restrictionType.equals(QueryUtil.GREATER_OR_EQUAL)) {
    							criterion[i] = Restrictions.ge(fieldName, fieldValue);
    						} else if (restrictionType.equals(QueryUtil.LESS_THAN)) {
    							criterion[i] = Restrictions.lt(fieldName, fieldValue);
    						} else if (restrictionType.equals(QueryUtil.LESS_OR_EQUAL)) {
    							criterion[i] = Restrictions.le(fieldName, fieldValue);
    						} else if (restrictionType.equals(QueryUtil.NOT_EQUAL)) {
    							criterion[i] = Restrictions.ne(fieldName, fieldValue);
    						} 
    					}
    				}
    				if (Validator.isNotEmpty(criterion)) {
    					criteria.add(Restrictions.and(criterion));
    				}
    				
    				return criteria.list();
    			}
    		} finally {
    			if (Validator.isNotNull(session) && session.isOpen()) {
    				session.close();
    			}
    		}
    	}
    	return null;
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    public int count() {
        CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
        Root<T> rt = cq.from(entityClass);
        cq.select(getEntityManager().getCriteriaBuilder().count(rt));
        Query q = getEntityManager().createQuery(cq);
        return ((Long) q.getSingleResult()).intValue();
    }

	public Class<T> getEntityClass() {
		return entityClass;
	}

	public void setEntityClass(Class<T> entityClass) {
		this.entityClass = entityClass;
	}
	
    protected EntityManager getEntityManager() {
    	return em;
    }
    
    protected Session getHibernateSession() {
    	SessionImpl sessionImpl = ((SessionImpl)getEntityManager().getDelegate());
    	if (Validator.isNotNull(sessionImpl)) {
    		return sessionImpl.getFactory().openSession();
    	} else {
    		return null;
    	}
    }
}
